package com.flow.framework.core.holder;

import com.flow.framework.common.error.SystemErrorCode;
import com.flow.framework.common.exception.CheckedException;
import com.flow.framework.common.json.JsonObject;
import com.flow.framework.common.util.verify.VerifyUtil;
import com.flow.framework.core.context.security.BaseUserContext;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 安全上下文持有者，包含用户鉴权信息、用户信息
 *
 * @author luoguopiao
 * @version 0.0.1
 * @date 2022/1/23
 */
@Slf4j
public class SecurityContextHolder {

    private static final ThreadLocal<ContextContent> CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL
            = ThreadLocal.withInitial(ContextContent::new);

    private static <T, R> R getValue(Function<ContextContent, R> callback) {
        ContextContent contextContent = CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get();
        if (null == contextContent) {
            return null;
        }
        return callback.apply(contextContent);
    }

    private static <T> void setValue(T value, BiConsumer<ContextContent, T> callback) {
        ContextContent contextContent = CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get();
        if (null == contextContent) {
            contextContent = new ContextContent();
            CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.set(contextContent);
        }
        callback.accept(contextContent, value);
    }

    /**
     * 获取鉴权信息
     *
     * @return 鉴权信息
     */
    public static String getAuthorization() {
        return getValue(ContextContent::getAuthorization);
    }

    /**
     * 设置鉴权信息
     *
     * @param authorization 鉴权信息
     */
    public static void setAuthorization(String authorization) {
        setValue(authorization, ContextContent::setAuthorization);
    }

    /**
     * 获取租户id
     *
     * @return 租户id
     */
    public static String getTenantIdQuietly() {
        return VerifyUtil.requireNotEmpty(getValue(ContextContent::getBaseUserContext)).getTenantId();
    }

    /**
     * 获取租户id
     *
     * @return 租户id
     */
    public static String getTenantId() {
        String tenantId = VerifyUtil.requireNotEmpty(getValue(ContextContent::getBaseUserContext)).getTenantId();
        if (null == tenantId) {
            log.error("tenant id is null.");
            throw new CheckedException(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
        }
        return tenantId;
    }

    /**
     * 设置租户id
     *
     * @param tenantId 租户id
     */
    public static void setTenantId(String tenantId) {
        setValue(tenantId, (contextContent, value) -> contextContent.getBaseUserContext().setTenantId(value));
    }

    /**
     * 获取用户id
     *
     * @return 用户id
     */
    public static String getUserIdQuietly() {
        return VerifyUtil.requireNotEmpty(getValue(ContextContent::getBaseUserContext)).getUserId();
    }

    /**
     * 获取用户id
     *
     * @return 用户id
     */
    public static String getUserId() {
        String userId = VerifyUtil.requireNotEmpty(getValue(ContextContent::getBaseUserContext)).getUserId();
        if (null == userId) {
            log.error("user id is null.");
            throw new CheckedException(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
        }
        return userId;
    }

    /**
     * 设置用户id
     *
     * @param userId 用户id
     */
    public static void setUserId(String userId) {
        setValue(userId, (contextContent, value) -> contextContent.getBaseUserContext().setUserId(value));
    }

    /**
     * 设置是否需要国际化
     *
     * @param i18nRequired 是否需要国际化
     */
    public static void setI18nRequired(boolean i18nRequired) {
        setValue(i18nRequired, (contextContent, value) -> contextContent.getBaseUserContext().setI18Required(value));
    }

    /**
     * 是否需要国际化
     *
     * @return 是否需要国际化
     */
    public static boolean isI18nRequired() {
        return VerifyUtil.requireNotEmpty(getValue(ContextContent::getBaseUserContext)).isI18Required();
    }

    /**
     * 该方法根据传入的UserContext的子类，返回对应的子类对象
     *
     * @param clazz 子类
     * @param <T>   子类对象
     * @return context
     */
    @Nullable
    public static <T extends BaseUserContext> T getUserContextQuietly(Class<T> clazz) {
        if (VerifyUtil.isEmpty(clazz)) {
            log.error("user context clazz is null.");
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR);
        }
        String userContextString = getValue(ContextContent::getUserContextString);
        if (VerifyUtil.isEmpty(userContextString)) {
            return null;
        }
        return JsonObject.toBean(userContextString, clazz);
    }

    /**
     * 该方法根据传入的UserContext的子类，返回对应的子类对象
     *
     * @param clazz 子类
     * @param <T>   子类对象
     * @return context
     */
    public static <T extends BaseUserContext> T getUserContext(Class<T> clazz) {
        if (VerifyUtil.isEmpty(clazz)) {
            log.error("user context clazz is null.");
            throw new CheckedException(SystemErrorCode.PARAMS_ERROR);
        }
        String userContextString = getValue(ContextContent::getUserContextString);
        if (VerifyUtil.isEmpty(userContextString)) {
            log.error("user context is null.");
            throw new CheckedException(SystemErrorCode.OBJECT_NOT_FOUND_ERROR);
        }
        return JsonObject.toBean(userContextString, clazz);
    }

    public static void setUserContext(String context) {
        setValue(context, (contextContent, value) -> {
            contextContent.setUserContextString(context);
            contextContent.setBaseUserContext(JsonObject.toBean(context, BaseUserContext.class));
        });
    }

    /**
     * 获取用户上下文json
     *
     * @return 用户上下文json
     */
    public static String getUserContextString() {
        return getValue(ContextContent::getUserContextString);
    }

    /**
     * 清空鉴权信息（鉴权信息和用户信息）
     */
    public static void clearAuthority() {
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().setBaseUserContext(new BaseUserContext());
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().setUserContextString(null);
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().setAuthorization(null);
    }

    /**
     * 清空用户信息
     */
    public static void clearUserContext() {
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().setBaseUserContext(new BaseUserContext());
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().setUserContextString(null);
    }

    /**
     * 清空租户id
     */
    public static void clearTenantId() {
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().getBaseUserContext().setTenantId(null);
    }

    /**
     * 清空用户is
     */
    public static void clearUserId() {
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().getBaseUserContext().setUserId(null);
    }

    /**
     * 清空国际化标识
     */
    public static void clearI18nRequired() {
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.get().getBaseUserContext().setI18Required(false);
    }

    /**
     * 清空安全上下文信息
     */
    public static void clearAll() {
        CONTEXT_HOLDER_CONTENT_CACHE_THREAD_LOCAL.remove();
    }

    /**
     * 上下文内容
     */
    @Data
    private static class ContextContent {

        /**
         * 用户鉴权信息
         */
        private String authorization;

        /**
         * 用户上下文信息（字符串）
         */
        private String userContextString;

        /**
         * 用户上下文信息基本对象
         */
        private BaseUserContext baseUserContext = new BaseUserContext();
    }
}
